Feat/model inputs dump#1118
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR adds an opt-in debug feature to dump model inputs into rotating log files to aid troubleshooting and profiling.
Changes:
- Introduces
enable_model_inputs_logconfig/CLI/env flag and propagates it through executors and gatherers. - Extends
GptModelInputswith extra tensors for logging snapshots and wires a newModelInputsLoggerintoPyWrappedModel::forward. - Implements
ModelInputsLoggerwith file rotation, periodic flush, and optional metrics reporting.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| rtp_llm/server/server_args/profile_debug_logging_group_args.py | Adds CLI/env switch to enable model input logging. |
| rtp_llm/cpp/config/ConfigModules.h | Adds enable_model_inputs_log to profiling debug config. |
| rtp_llm/cpp/config/ConfigModules.cc | Prints the new config field in to_string(). |
| rtp_llm/cpp/pybind/ConfigInit.cc | Exposes the new config field to Python + extends pickle state. |
| rtp_llm/models_py/bindings/core/OpData.h | Adds host snapshot tensors to GptModelInputs for logging. |
| rtp_llm/cpp/normal_engine/NormalModelInputGatherer.h | Adds gatherer flag to control host snapshots for logging. |
| rtp_llm/cpp/normal_engine/NormalModelInputGatherer.cc | Populates logging snapshot tensors when enabled. |
| rtp_llm/cpp/normal_engine/NormalBatchStreamProcessor.cc | Passes the new flag into the input gatherer config. |
| rtp_llm/cpp/normal_engine/NormalExecutor.h | Stores a shared ModelInputsLogger in the executor. |
| rtp_llm/cpp/normal_engine/NormalExecutor.cc | Conditionally constructs/injects ModelInputsLogger into model. |
| rtp_llm/cpp/normal_engine/speculative/MtpExecutor.h | Stores a shared ModelInputsLogger for speculative executor. |
| rtp_llm/cpp/normal_engine/speculative/MtpExecutor.cc | Conditionally constructs/injects ModelInputsLogger into models. |
| rtp_llm/cpp/models/PyWrappedModel.h | Adds logger dependency to constructor and stores it. |
| rtp_llm/cpp/models/PyWrappedModel.cc | Emits model inputs logs at start of forward(). |
| rtp_llm/cpp/models/ModelInputsLogger.h | Adds new logger class interface. |
| rtp_llm/cpp/models/ModelInputsLogger.cc | Implements JSONL logging + rotation + metrics. |
| rtp_llm/cpp/models/BUILD | Adds build deps needed by ModelInputsLogger. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| output_.open(file_path_, std::ios::out | std::ios::trunc); | ||
| bytes_ = 0; | ||
| } | ||
|
|
||
| std::mutex mutex_; | ||
| std::filesystem::path file_path_; | ||
| std::ofstream output_; | ||
| int backup_count_ = 0; | ||
| size_t bytes_ = 0; | ||
| size_t pending_lines_ = 0; | ||
| int64_t last_flush_us_ = 0; | ||
| bool valid_ = false; |
| std::string jsonEscape(const std::string& input) { | ||
| std::ostringstream os; | ||
| for (unsigned char c : input) { | ||
| switch (c) { | ||
| case '\\': | ||
| os << "\\\\"; | ||
| break; | ||
| case '"': | ||
| os << "\\\""; | ||
| break; | ||
| case '\n': | ||
| os << "\\n"; | ||
| break; | ||
| case '\r': | ||
| os << "\\r"; | ||
| break; | ||
| case '\t': | ||
| os << "\\t"; | ||
| break; | ||
| default: | ||
| os << static_cast<char>(c); | ||
| break; | ||
| } | ||
| } | ||
| return os.str(); | ||
| } |
| std::string combineStringsForLog(const std::vector<std::string>& vec) { | ||
| std::string result = "\" "; | ||
| for (const auto& s : vec) { | ||
| result += s + ", "; | ||
| } | ||
| result += "\""; | ||
| return result; | ||
| } |
| profile_debug_logging_group.add_argument( | ||
| "--enable_model_inputs_log", | ||
| env_name="ENABLE_MODEL_INPUTS_LOG", | ||
| bind_to=(profiling_debug_config, "enable_model_inputs_log"), | ||
| type=str2bool, | ||
| default=False, | ||
| help="控制是否打印模型输入日志。可选值: True (启用), False (禁用)。默认为 False", | ||
| ) |
| torch::Tensor combo_tokens_host_for_log; | ||
| torch::Tensor input_lengths_host_for_log; | ||
| torch::Tensor sequence_lengths_host_for_log; | ||
| torch::Tensor prefix_lengths_host_for_log; |
6eab357 to
c8deafc
Compare
AI Code Review - PR #1118Status: LGTM Summary: P0/0 · P1/0 · P2/3 · P3/0 lgtm ready to ci Non-blocking SuggestionsP2
Checklist Violations (6 fail / 78 total)General Principles Checklist
RTP-LLM Checklist
Strengths
|
| return pos == std::string::npos ? tensor_with_data : tensor_with_data.substr(pos + 2); | ||
| } | ||
|
|
||
| std::string tensorLogString(const torch::Tensor& tensor, const torch::Tensor& host_snapshot = {}) { |
| @@ -0,0 +1,271 @@ | |||
| #include "rtp_llm/cpp/models/ModelInputsLogger.h" | |||
There was a problem hiding this comment.
正常调用都是在模型执行之前打印的日志,relay也可以打印出来
c8deafc to
aacc636
Compare
aacc636 to
c8deafc
Compare
| const auto server_id = autil::EnvUtil::getEnv("FRONTEND_SERVER_ID", 0); | ||
| file_path_ = log_dir / ("model_inputs_r" + std::to_string(rank_id) + "_s" + std::to_string(server_id) + ".log"); | ||
| bytes_ = std::filesystem::exists(file_path_, ec) ? std::filesystem::file_size(file_path_, ec) : 0; | ||
| output_.open(file_path_, std::ios::out | std::ios::app); |
| std::lock_guard<std::mutex> guard(mutex_); | ||
| rotateIfNeeded(line.size() + 1); | ||
| output_ << line << '\n'; | ||
| bytes_ += line.size() + 1; |
AI Code Review - PR #1118Status: BLOCKING Summary: P0/0 · P1/1 · P2/3 · P3/2 Blocking IssuesP1
Non-blocking SuggestionsP2
P3
Checklist Violations (7 fail / 78 total)General Principles Checklist
RTP-LLM Checklist
Python Static-First Checklist
Strengths
|
AI Code Review - PR #1118Status: BLOCKING Summary: P0/0 · P1/1 · P2/0 · P3/1 Blocking IssuesP1
Non-blocking SuggestionsP3
Checklist Violations (5 fail / 74 total)RTP-LLM Checklist
Python Static-First Checklist
Strengths
|
934e16a to
379615f
Compare
AI Code Review - PR #1118Status: BLOCKING Summary: P0/0 · P1/0 · P2/5 · P3/1 Non-blocking SuggestionsP2
P3
Checklist Violations (6 fail / 56 total)General Principles Checklist
Python Static-First Checklist
Strengths
|
379615f to
1678e56
Compare
| max_total_tokens: int = 4096, | ||
| tokens_per_block: int = 64, | ||
| kernel_tokens_per_block: int = 0, | ||
| stop_id_list: list[int] = [], |
| ratios = list(self.model_config.attn_config.layer_compress_ratios) | ||
| kernel_tokens = self.kv_cache.kernel_seq_size_per_block | ||
| physical_tokens = self.kv_cache.seq_size_per_block | ||
| kernel_blocks_per_kv_block = max(1, physical_tokens // kernel_tokens) | ||
| head_dim = self.model_config.attn_config.size_per_head |
| attention_inputs.kv_cache_kernel_block_id = ( | ||
| attention_inputs.kv_cache_block_id | ||
| ) | ||
| attention_inputs.kv_cache_kernel_block_id = attention_inputs.kv_cache_block_id |
| attention_inputs.kv_cache_block_id_device = torch.tensor( | ||
| [[i for i in range(1, need_block_nums + 1)]], | ||
| dtype=torch.int32, | ||
| device=self.device, | ||
| ) | ||
| attention_inputs.kv_cache_kernel_block_id_device = ( | ||
| attention_inputs.kv_cache_block_id_device | ||
| ) | ||
| attention_inputs.kv_cache_block_id = torch.tensor( | ||
| [[i for i in range(1, need_block_nums + 1)]], dtype=torch.int32 | ||
| ) | ||
| attention_inputs.kv_cache_kernel_block_id = ( | ||
| attention_inputs.kv_cache_block_id | ||
| ) | ||
| attention_inputs.kv_cache_kernel_block_id = attention_inputs.kv_cache_block_id | ||
| attention_inputs.dtype = get_typemeta(self.kv_cache.kv_cache_base_by_layer[0]) |
| rtp_llm::ModelInputsLogger logger(/*rank_id=*/2, /*backup_count=*/1, nullptr); | ||
| for (int i = 0; i < dump_count; ++i) { | ||
| logger.log(rtp_llm::makeInputs()); | ||
| } |
1678e56 to
1818574
Compare
AI Code Review - PR #1118Status: BLOCKING Summary: P0/0 · P1/0 · P2/5 · P3/3 Non-blocking SuggestionsP2
P3
Checklist Violations (12 fail / 56 total)General Principles Checklist
Python Static-First Checklist
Strengths
|
1818574 to
e42739f
Compare
| from rtp_llm.ops.compute_ops import ( | ||
| CacheGroupType, | ||
| KVCache, | ||
| KVCacheRegionName, | ||
| PyAttentionInputs, |
| # Explicitly mark every layer as full-attention. | ||
| self.kv_cache.layer_attn_types = [ | ||
| self.kv_cache.layer_group_types = [ | ||
| CacheGroupType.FULL for _ in range(self.layer_num) | ||
| ] |
| self.kv_cache.group_region_names = region_order | ||
| self.kv_cache.group_seq_size_per_block = [physical_tokens for _ in region_order] | ||
| self.kv_cache.layer_region_to_group_id = [ | ||
| [-1 for _ in range(region_count)] for _ in range(self.layer_num) | ||
| ] |
| self.kv_cache.layer_group_types = [ | ||
| CacheGroupType.SWA for _ in range(self.layer_num) | ||
| ] |
e42739f to
07ce2ed
Compare
07ce2ed to
4ac7dcb
Compare
4ac7dcb to
c7fef42
Compare
| namespace rtp_llm { | ||
| namespace { | ||
|
|
||
| GptModelInputs makeInputs() { | ||
| GptModelInputs inputs; | ||
| inputs.trace_ids = {"trace-a", "trace-b"}; | ||
| inputs.combo_tokens = torch::tensor({11, 12, 13}, torch::kInt32); | ||
| inputs.input_lengths = torch::tensor({3}, torch::kInt32); | ||
| inputs.sequence_lengths = torch::tensor({2}, torch::kInt32); | ||
| inputs.lm_output_indexes = torch::tensor({2}, torch::kInt32); | ||
| inputs.prefix_lengths = torch::tensor({1}, torch::kInt32); | ||
| inputs.combo_tokens_type_ids = torch::tensor({0, 0, 1}, torch::kInt32); | ||
| inputs.combo_position_ids = torch::tensor({0, 1, 2}, torch::kInt32); | ||
| inputs.kv_cache_block_id = torch::tensor({{{7, 8}}}, torch::kInt32); | ||
| inputs.kv_cache_layer_to_group = torch::tensor({0}, torch::kInt32); | ||
| inputs.kv_cache_group_types = torch::tensor({1}, torch::kInt32); | ||
| inputs.kv_cache_update_mapping = torch::tensor({{1, 2}}, torch::kInt32); | ||
| inputs.request_id = torch::tensor({12345}, torch::kInt64); | ||
| inputs.request_pd_separation = torch::tensor({false}, torch::kBool); | ||
| inputs.kv_block_stride_bytes = 4096; | ||
| inputs.kv_scale_stride_bytes = 128; | ||
| inputs.seq_size_per_block = 64; | ||
| inputs.kernel_seq_size_per_block = 32; | ||
| inputs.decode_entrance = true; | ||
| inputs.need_all_logits = true; | ||
| inputs.need_moe_gating = true; | ||
| return inputs; | ||
| } | ||
|
|
||
| } // namespace | ||
| } // namespace rtp_llm | ||
|
|
||
| int main(int argc, char** argv) { | ||
| if (argc != 2 && argc != 3) { | ||
| std::cerr << "usage: " << argv[0] << " <log_dir> [dump_count]" << std::endl; | ||
| return 2; | ||
| } | ||
|
|
||
| const auto dump_count = argc == 3 ? std::stoi(argv[2]) : 1; | ||
| setenv("LOG_PATH", argv[1], 1); | ||
| setenv("FRONTEND_SERVER_ID", "3", 1); | ||
| rtp_llm::ModelInputsLogger logger(/*rank_id=*/2, /*backup_count=*/1, nullptr); | ||
| for (int i = 0; i < dump_count; ++i) { | ||
| logger.log(rtp_llm::makeInputs()); | ||
| } | ||
| return 0; | ||
| } |
| void rotateIfNeeded(size_t next_bytes) { | ||
| if (bytes_ == 0 || bytes_ + next_bytes <= kModelInputsDumpMaxBytes) { | ||
| return; | ||
| } | ||
|
|
||
| output_.close(); | ||
| const auto rotated_path = | ||
| output_dir_ / (prefix_ + "_" + timestampForFile(autil::TimeUtility::currentTimeInMicroSeconds()) + ".pt"); | ||
| std::error_code ec; | ||
| std::filesystem::rename(file_path_, rotated_path, ec); | ||
| if (ec) { | ||
| RTP_LLM_LOG_WARNING("Failed to rotate model inputs dump file from %s to %s: %s", | ||
| file_path_.c_str(), | ||
| rotated_path.c_str(), | ||
| ec.message().c_str()); | ||
| } | ||
| cleanupOldFiles(); | ||
| bytes_ = 0; | ||
| pending_records_ = 0; | ||
| open(std::ios::out | std::ios::binary | std::ios::trunc); | ||
| } |
| void cleanupOldFiles() { | ||
| if (backup_count_ <= 0) { | ||
| return; | ||
| } | ||
|
|
||
| std::vector<std::filesystem::directory_entry> rotated_files; | ||
| std::error_code ec; | ||
| for (const auto& entry : std::filesystem::directory_iterator(output_dir_, ec)) { | ||
| if (ec || !entry.is_regular_file()) { | ||
| continue; | ||
| } | ||
| const auto name = entry.path().filename().string(); | ||
| if (name.rfind(prefix_ + "_", 0) == 0 && entry.path().extension() == ".pt") { | ||
| rotated_files.push_back(entry); | ||
| } | ||
| } | ||
| if (rotated_files.size() <= static_cast<size_t>(backup_count_)) { | ||
| return; | ||
| } | ||
| std::sort(rotated_files.begin(), rotated_files.end(), [](const auto& lhs, const auto& rhs) { | ||
| return lhs.path().filename().string() < rhs.path().filename().string(); | ||
| }); | ||
| const auto remove_count = rotated_files.size() - static_cast<size_t>(backup_count_); | ||
| for (size_t i = 0; i < remove_count; ++i) { | ||
| std::filesystem::remove(rotated_files[i], ec); | ||
| } | ||
| } |
| if (config_.enable_model_inputs_log) { | ||
| model_input.prefix_lengths_host_for_log = model_input.prefix_lengths; | ||
| } |
AI Code Review - PR #1118Status: BLOCKING Summary: P0/0 · P1/0 · P2/3 · P3/2 Non-blocking SuggestionsP2
P3
Checklist Violations (5 fail / 56 total)General Principles Checklist
Strengths
|
No description provided.